package uk.co.g4me.frameworks.spring.roo.addons.shiro;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.addon.jpa.JpaOperations;
import org.springframework.roo.addon.web.mvc.controller.ControllerOperations;
import org.springframework.roo.addon.web.mvc.controller.WebMvcOperations;
import org.springframework.roo.addon.web.mvc.jsp.tiles.TilesOperations;
import org.springframework.roo.classpath.TypeLocationService;
import org.springframework.roo.classpath.TypeManagementService;
import org.springframework.roo.classpath.customdata.CustomDataKeys;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.metadata.MetadataService;
import org.springframework.roo.model.JavaPackage;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.process.manager.MutableFile;
import org.springframework.roo.project.Dependency;
import org.springframework.roo.project.DependencyScope;
import org.springframework.roo.project.DependencyType;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.PathResolver;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.project.Property;
import org.springframework.roo.project.maven.Pom;
import org.springframework.roo.shell.Shell;
import org.springframework.roo.support.logging.HandlerUtils;
import org.springframework.roo.support.util.DomUtils;
import org.springframework.roo.support.util.FileUtils;
import org.springframework.roo.support.util.WebXmlUtils;
import org.springframework.roo.support.util.XmlElementBuilder;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import uk.co.g4me.frameworks.spring.roo.addons.shiro.enums.RealmType;

/**
 * Implementation of operations this add-on offers.
 * 
 * @since 0.1
 */
@Component
// Use these Apache Felix annotations to register your commands class in the Roo
// container
@Service
public class ShiroOperationsImpl implements ShiroOperations {

	Logger log = HandlerUtils.getLogger(getClass());

	private static final String SHIRO_VERSION = "1.2.1";
	//TODO: handle itegration with spring social
	// private static final String SPRING_SOCIAL_VERSION = "1.0.1.RELEASE";
	private static final String FILTER_NAME											=	"shiroFilter";
	
	private static final String PERSISTENCE_XML 									=	"META-INF/persistence.xml";
	
	private static final String MONGO_XML											=	"applicationContext-mongo.xml";	

	private static final String REALM_PACKAGE_REPLACEMENT_STRING 					=	"_REPLACE_THIS_STRING_WITH_REALM_TARGET_PACKAGE_";
	
	private static final String ENTITY_PACKAGE_REPLACEMENT_STRING 					=	"_REPLACE_THIS_STRING_WITH_ENTITY_TARGET_PACKAGE_";
	
	private static final String REPOSITORY_PACKAGE_REPLACEMENT_STRING 				=	"_REPLACE_THIS_STRING_WITH_REPOSITORY_TARGET_PACKAGE_";
	
	private static final String REPOSITORY_ANNOTATION_PACKAGE_REPLACEMENT_STRING 	=	"_REPLACE_THIS_STRING_WITH_REPOSITORY_ANNOTATION_PACKAGE_";
	
	private static final String REPOSITORY_ANNOTATION_NAME_REPLACEMENT_STRING 		=	"_REPLACE_THIS_STRING_WITH_REPOSITORY_ANNOTATION_NAME_";
	
	private RealmType persistenceType;

	/**
	 * Use ProjectOperations to install new dependencies, plugins, properties,
	 * etc into the project configuration
	 */
	@Reference
	private ProjectOperations projectOperations;

	@Reference
	private ControllerOperations controllerOperations;

	@Reference
	private JpaOperations jpaOperaions;

	@Reference
	private PathResolver pathResolver;

	@Reference
	private FileManager fileManager;

	@Reference
	private TilesOperations tilesOperations;

	@Reference
	private MetadataService metadataService;

	@Reference
	private TypeLocationService typeLocationService;

	/**
	 * Use TypeManagementService to change types
	 */
	@Reference
	private TypeManagementService typeManagementService;

	@Reference
	private Shell shell;

	private Set<ClassOrInterfaceTypeDetails> entities;

	private String entityPackage;

	private String controllerPackage;

	private Pom webModule;

	private Pom entityModule;

	

	/** {@inheritDoc} */
	public boolean isSetupCommandAvailable() {
		return projectOperations.isModuleCreationAllowed() && lacksDependency("spring-security-core") && lacksDependency("shiro-core");
//		return projectOperations.isModuleCreationAllowed() && hasPersistenceTier() && hasWebTier();
	}

	private boolean hasPersistenceTier() {
		return moduleHasFile(entityModule, Path.SPRING_CONFIG_ROOT, "database.properties", "No persistence tier found.");
	}
	
	private boolean hasWebTier() {
		return moduleHasFile(webModule, Path.SRC_MAIN_WEBAPP, "/WEB-INF/web.xml", "No web tier found.");
	}
	
	
	//TODO: refactor into utiities jar
	private boolean moduleHasFile(Pom module, Path moduleSubPath, String relativePath) {
		return moduleHasFile(module, moduleSubPath, relativePath, null);
	}

	private boolean moduleHasFile(Pom module, Path moduleSubPath, String relativePath, String message) {
		//TODO: Add support for monolithic projects. rename to projectHasFile;
		Pom currentModule = projectOperations.getFocusedModule();
		projectOperations.setModule(module);
		if (fileManager.exists(projectOperations.getPathResolver().getFocusedIdentifier(moduleSubPath, relativePath))) {
			projectOperations.setModule(currentModule);
			return true;
		} else {
			if (message != null) {
				log.log(Level.WARNING, "Unavailable: shiro setup: modulesHaveFile() " + message);
			}			
			return false;
		}		
	}

	//TODO: Refactor into utulities jar
	private boolean lacksDependency(String artifactId) {
		Pom currentModule = projectOperations.getFocusedModule();
			for (Dependency dependency : currentModule.getDependencies()) {
				if (artifactId.toLowerCase().equals(dependency.getArtifactId().toLowerCase())) {
					log.log(Level.WARNING,
							"Unavailable: shiro setup: lacksDependency("
									+ artifactId + "): False: "
									+ currentModule + " has " + artifactId
									+ " installed.");
					projectOperations.setModule(currentModule);
					return false;
				} else {
					continue;
				}
			}
		return true;
	}

	/** {@inheritDoc} */
	public void setup(Pom entityModule, Pom webModule,
		String entityPackage, String controllerPackage) {
		//TODO: Implement shiro annotation support
		// TODO: General cleanup of project flow
		
		this.entityPackage = entityPackage;

		this.controllerPackage = controllerPackage;

		this.webModule = webModule;

		this.entityModule = entityModule;
		
		if (!hasPersistenceTier()) {
			return;
		}
		
		if (!hasWebTier()) {
			return;
		}
		
		shell.executeCommand("module focus --moduleName ~");		
		
		shell.executeCommand("module create --moduleName security --topLevelPackage ~.security");
		
		projectOperations.addProperty("security", new Property("shiro.version", SHIRO_VERSION));

		List<Dependency> dependencies = new ArrayList<Dependency>();
		
		//TODO: Ensure only the required dependencies are installed into relevant modules
		// Install the shiro dependencies
		dependencies.add(addDependency("org.apache.shiro", "shiro-aspectj", "${shiro.version}"));
		dependencies.add(addDependency("org.apache.shiro", "shiro-core", "${shiro.version}"));
		dependencies.add(addDependency("org.apache.shiro", "shiro-ehcache", "${shiro.version}"));
		dependencies.add(addDependency("org.apache.shiro", "shiro-spring", "${shiro.version}"));
		dependencies.add(addDependency("org.apache.shiro", "shiro-web", "${shiro.version}"));
		dependencies.add(addDependency(entityModule.getGroupId(), entityModule.getArtifactId(), entityModule.getVersion()));		

		// Add all new dependencies to pom.xml
		projectOperations.addDependencies("security", dependencies);

		pathResolver = projectOperations.getPathResolver();
		
		copyFile("applicationContext-security-template.xml",
				pathResolver.getFocusedIdentifier(Path.SPRING_CONFIG_ROOT,
						"applicationContext-security.xml"));
		
		if (jpaIsInstalled(entityModule.getModuleName())) {
			persistenceType = RealmType.JPA;
		}
		
		if (mongoIsInstalled(entityModule.getModuleName())) {
			persistenceType = RealmType.MONGODB;
		}
		
		if (persistenceType == null) {
			log.log(Level.WARNING, "Persistence tier not detected, expecting " + PERSISTENCE_XML + " or " + MONGO_XML + ".");
			
		}
		
		switch (persistenceType) {
		case JPA:
			addRealm(RealmType.JPA);
			setupEntities(RealmType.JPA);
			break;
		case MONGODB:
			addRealm(RealmType.MONGODB);
			setupEntities(RealmType.MONGODB);
			break;
		default:
			log.log(Level.WARNING, "Persistence tier not detected, expecting " + PERSISTENCE_XML + " or " + MONGO_XML + ".");
			break;		
		}
		
		setupControllers();
		
		setupFilter();		

		setupLoginPage();		
		
		addDependencyToUi();

	}
	
	private boolean mongoIsInstalled(String focusedModuleName) {
		return projectOperations.isFocusedProjectAvailable()
                && fileManager.exists(pathResolver.getFocusedIdentifier(
                        Path.SPRING_CONFIG_ROOT, MONGO_XML));
	}

	private boolean jpaIsInstalled(String focusedModuleName) {
		final LogicalPath resourcesPath = LogicalPath.getInstance(
                Path.SRC_MAIN_RESOURCES, focusedModuleName);
        return isSetupCommandAvailable()
                && fileManager.exists(projectOperations.getPathResolver()
                        .getIdentifier(resourcesPath, PERSISTENCE_XML));
	}

	private void copyFile(String file, String destination) {
		copyMutableFile(file, destination, null);
	}
	
	private void copyMutableFile(String template, String destination, Map<String, String> fileMutations) {
		//TODO: Refactor into a utilities jar
		InputStream in = null;
		OutputStream out = null;
		
		File fileDestination = new File(destination);
		// Create parent directories of they don't exist
		fileDestination.getParentFile().mkdirs();
		
		if (!fileManager.exists(destination)) {
			try {			
				in = FileUtils.getInputStream(getClass(), template);
				out = new FileOutputStream(fileDestination);
				
				if (fileMutations != null) {
					String content = IOUtils.toString(in);
					for (String key : fileMutations.keySet()) {
						content = content.replaceAll(key, fileMutations.get(key));
					}
					in = IOUtils.toInputStream(content);
				}
				
				IOUtils.copy(in, out);				
			}	catch (IOException ioe) {
					throw new IllegalStateException(ioe);
			} finally {
				IOUtils.closeQuietly(in);
				IOUtils.closeQuietly(out);
			}
		} 
	}
	
	private void addRealm(RealmType realmType) {
		//TODO: Dynamic lass names needed, lets get the copy working first. 
		Pom securityPom = projectOperations.getPomFromModuleName("security");

		projectOperations.setModule(securityPom);
		
		String applicationContextPath = projectOperations.getPathResolver()
				.getFocusedIdentifier(Path.SPRING_CONFIG_ROOT,
						"applicationContext-security.xml");
		Document applicationContextDocument = XmlUtils.readXml(fileManager
				.getInputStream(applicationContextPath));

		Element root = (Element) applicationContextDocument.getFirstChild();
		Element realms = XmlUtils
				.findFirstElement(
						"/beans/bean[@class='org.apache.shiro.web.mgt.DefaultWebSecurityManager']/property[@name='realms']/list",
						root);

		Element realmBean = applicationContextDocument.createElement("bean");

		switch (realmType) {
		case JPA:
			realmBean.setAttribute("id", "persistenceRealm");
			realmBean.setAttribute("class",
					projectOperations.getTopLevelPackage(securityPom.getArtifactId()) + ".realms.PersistenceRealm");
			copyRealmClass(RealmType.JPA);
			break;
		case MONGODB:
			realmBean.setAttribute("id", "persistenceRealm");
			realmBean.setAttribute("class",
					projectOperations.getTopLevelPackage(securityPom.getArtifactId()) + ".realms.PersistenceRealm");
			copyRealmClass(RealmType.MONGODB);
			break;
		default:
			//TODO: shit yourself
			break;
		}
		
		realms.appendChild(realmBean);

		fileManager.createOrUpdateTextFileIfRequired(applicationContextPath,
				XmlUtils.nodeToString(applicationContextDocument), false);
	}

	private void copyRealmClass(RealmType realmType) {
		//TODO: Refactor into a utilities class
		//TODO: This is very messy and needs handling by type. Tired Zzzzzz
				
		String destination = pathResolver.getFocusedIdentifier(Path.SRC_MAIN_JAVA,projectOperations.getTopLevelPackage("security").toString().replace(".", "\\") + "\\realms\\PersistenceRealm.java");

		Map<String, String> fileMutations = new HashMap<String, String>();
		
		fileMutations.put(REALM_PACKAGE_REPLACEMENT_STRING, projectOperations.getTopLevelPackage("security").toString() + ".realms");
		//TODO: Check entity package name mungeing
		
		fileMutations.put(ENTITY_PACKAGE_REPLACEMENT_STRING, projectOperations.getTopLevelPackage(entityModule.getModuleName()) + entityPackage.replace("~", ""));
		
		fileMutations.put(REPOSITORY_ANNOTATION_PACKAGE_REPLACEMENT_STRING, projectOperations.getTopLevelPackage(entityModule.getModuleName()) + ".repository");
		
		//TODO: refactor code to have one clear instance of target packages/paths
		fileMutations.put(REPOSITORY_PACKAGE_REPLACEMENT_STRING, projectOperations.getTopLevelPackage(entityModule.getModuleName()) + ".repository");
		
		switch (realmType) {
		case JPA:
			copyMutableFile("PersistenceRealm.java", destination, fileMutations);
			break;
		case MONGODB:
			copyMutableFile("PersistenceRealm.java", destination, fileMutations);
			break;
		default:
			break;
		
		}		
	}
	
	private void setupEntities(RealmType realmType) {
		// TODO: Adapt for use with other persistence methods offered
		// by ROO
		// TODO: Change entity names to be type safe
		// TODO: Investigate seeding of database with users/roles/permissions

		entities = new HashSet<ClassOrInterfaceTypeDetails>();
		
		String entityCommand			=	null;
		String userEntityCommand		=	null;
		String roleEntityCommand		=	null;
		String permissionEntityCommand	=	null;
		String repositoryCommand		=	null;
		String annotationPackage		=	null;
		String annotationName			=	null;		

		//TODO: Pointless grab, just use the POM
		Pom entityPom = projectOperations
				.getPomFromModuleName(entityModule.getDisplayName());

		projectOperations.setModule(entityPom);		
		
		switch (realmType) {
		case JPA:
			entityCommand			=	"entity jpa";
			permissionEntityCommand	=	entityCommand + " --class " + entityPackage + ".Permission --table permissions --permitReservedWords";
			roleEntityCommand		=	entityCommand + " --class " + entityPackage + ".Role --table roles --permitReservedWords";
			userEntityCommand		=	entityCommand + " --class " + entityPackage	+ ".User --table users --permitReservedWords";
			annotationPackage		=	"org.springframework.roo.addon.layers.repository.jpa";
			annotationName			=	"RooJpaRepository";
			repositoryCommand	=	"repository jpa";
			break;
		case MONGODB:
			entityCommand			=	"entity mongo";
			permissionEntityCommand	=	entityCommand + " --class " + entityPackage + ".Permission";
			roleEntityCommand		=	entityCommand + " --class " + entityPackage + ".Role";
			userEntityCommand		=	entityCommand + " --class " + entityPackage + ".User";
			annotationPackage		=	"org.springframework.roo.addon.layers.repository.mongo";
			annotationName			=	"RooMongoRepository";
			repositoryCommand	=	"repository mongo";
			break;
		default:
			break;
		
		}
		
		shell.executeCommand(permissionEntityCommand);
		shell.executeCommand("field string --fieldName string --sizeMin 1 --notNull --unique");
		
		shell.executeCommand(roleEntityCommand);
		shell.executeCommand("field string --fieldName name --sizeMin 1 --notNull");
		shell.executeCommand("field set --fieldName permissions --type " + entityPackage + ".Permission");
		
		shell.executeCommand(userEntityCommand);
		shell.executeCommand("field string --fieldName username --sizeMin 1 --notNull --unique");
		shell.executeCommand("field string --fieldName password --sizeMin 1 --sizeMax 200 --notNull");
		shell.executeCommand("field set --fieldName roles --type " + entityPackage + ".Role");
		
		shell.executeCommand(repositoryCommand + " --interface ~.repository.RoleRepository --entity " + entityPackage + ".Role");
		shell.executeCommand(repositoryCommand + " --interface ~.repository.PermissionRepository --entity " + entityPackage + ".Permission");
				
		projectOperations.setModule(entityModule);
		
		String destination = pathResolver.getFocusedIdentifier(Path.SRC_MAIN_JAVA,projectOperations.getTopLevelPackage(entityModule.getModuleName()).toString().replace(".", "\\") + "\\repository\\UserRepository.java");
		
		Map<String, String> fileMutations = new HashMap<String, String>();
		
		fileMutations.put(REPOSITORY_PACKAGE_REPLACEMENT_STRING, projectOperations.getTopLevelPackage(entityModule.getModuleName()) + ".repository");
		fileMutations.put(ENTITY_PACKAGE_REPLACEMENT_STRING, projectOperations.getTopLevelPackage(entityModule.getModuleName()) + entityPackage.replace("~", ""));
		fileMutations.put(REPOSITORY_ANNOTATION_PACKAGE_REPLACEMENT_STRING, annotationPackage);
		fileMutations.put(REPOSITORY_ANNOTATION_NAME_REPLACEMENT_STRING, annotationName);
				
		// copy the repository interface
		copyMutableFile("UserRepository.java", destination, fileMutations);

		for (ClassOrInterfaceTypeDetails entityDetail : typeLocationService.findClassesOrInterfaceDetailsWithTag(CustomDataKeys.PERSISTENT_TYPE)) {
			if(entityDetail.getType().getSimpleTypeName().equals("User") || entityDetail.getType().getSimpleTypeName().equals("Role") || entityDetail.getType().getSimpleTypeName().equals("Permission")) {
				entities.add(entityDetail);
			}			
		}
	}
	
	private void setupControllers() {
		// TODO: Adapt for use with GAE and other views offered by ROO
		// TODO: Refactor for cleaner entity management.

		projectOperations.setModule(webModule);	
		
		for (ClassOrInterfaceTypeDetails entityDetail : entities) {
			JavaType entity = entityDetail.getType();
			JavaPackage entityPackage = entity.getPackage();
			shell.executeCommand("web mvc scaffold " +
									"--class " + controllerPackage + "." + entity.getSimpleTypeName() + "Controller " + 
									"--backingType " + entityPackage + "." + entity.getSimpleTypeName()
								);	
		}		
	}
	
	private void setupFilter() {
		// TODO: Adapt for use with GAE and other views offered by ROO
		// TODO: Update POM to use
		// org.springframework.roo.addon.web.mvc.controller.WebMvcOperations
		// version 1.2.2 currently has issues
		
		projectOperations.setModule(webModule);

		String webXmlPath = projectOperations.getPathResolver()
				.getFocusedIdentifier(Path.SRC_MAIN_WEBAPP, "WEB-INF/web.xml");
		Document webXmlDocument = XmlUtils.readXml(fileManager
				.getInputStream(webXmlPath));

		WebXmlUtils.addFilterAtPosition(WebXmlUtils.FilterPosition.BETWEEN,
				WebMvcOperations.HTTP_METHOD_FILTER_NAME,
				WebMvcOperations.OPEN_ENTITYMANAGER_IN_VIEW_FILTER_NAME,
				FILTER_NAME,
				"org.springframework.web.filter.DelegatingFilterProxy", "/*",
				webXmlDocument, null);
		fileManager.createOrUpdateTextFileIfRequired(webXmlPath,
				XmlUtils.nodeToString(webXmlDocument), false);
	}
	
	private void setupLoginPage() {
		// TODO: Adapt for use with GAE and other views offered by ROO
		// Copy the template across

		projectOperations.setModule(webModule);

		String loginPage = pathResolver.getFocusedIdentifier(
				Path.SRC_MAIN_WEBAPP, "WEB-INF/views/login.jspx");
		
		copyFile("login.jspx", loginPage);

		// Add the login page to the tiles configuration.
		if (fileManager.exists(pathResolver.getFocusedIdentifier(
				Path.SRC_MAIN_WEBAPP, "/WEB-INF/views/views.xml"))) {
			tilesOperations.addViewDefinition("",
					pathResolver.getFocusedPath(Path.SRC_MAIN_WEBAPP), "login",
					TilesOperations.PUBLIC_TEMPLATE,
					"/WEB-INF/views/login.jspx");
		}

		// Include static view controller handler to webmvc-config.xml
		String webConfigPath = projectOperations.getPathResolver()
				.getFocusedIdentifier(Path.SRC_MAIN_WEBAPP,
						"WEB-INF/spring/webmvc-config.xml");
		Document webConfigDocument = XmlUtils.readXml(fileManager
				.getInputStream(webConfigPath));
		Element webConfig = webConfigDocument.getDocumentElement();
		
		Element viewController = DomUtils.findFirstElementByName(
				"mvc:view-controller", webConfig);

		Validate.notNull(viewController,
				"Could not find mvc:view-controller in " + webConfig);
		viewController.getParentNode()
				.insertBefore(
						new XmlElementBuilder("mvc:view-controller",
								webConfigDocument).addAttribute("path",
								"/login").build(), viewController);
		fileManager.createOrUpdateTextFileIfRequired(webConfigPath,
				XmlUtils.nodeToString(webConfigDocument), false);

		//TODO: Edit the existing security string in messages.properties
		insertMessages("shiro_security_login_title", "Shiro Security Login");
		insertMessages("security_login_form_remember_me", "Remember Me");
		insertMessages("security_login_form_remember_me_message", "Remember my login on this computer");
	}

	private void insertMessages(String key, String value) {
		//TODO: Refactor into a utilities class
		//TODO: Refactor to allow changing strings
		// TODO: Alert if there are more localisations
		String applicationProperties = pathResolver.getFocusedIdentifier(
				Path.SRC_MAIN_WEBAPP, "WEB-INF/i18n/messages.properties");

		MutableFile mutableApplicationProperties = null;

		try {
			if (fileManager.exists(applicationProperties)) {
				mutableApplicationProperties = fileManager
						.updateFile(applicationProperties);
				
				String originalData = IOUtils.toString(mutableApplicationProperties.getInputStream());
				
				BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
						mutableApplicationProperties.getOutputStream()));

				out.write(originalData);
				out.write("\n" + key + "=" + value);

				out.close();

			} else {
				throw new IllegalStateException("Could not acquire "
						+ applicationProperties);
			}
		} catch (Exception e) {
			log.info("---> " + e.getMessage());
			throw new IllegalStateException(e);
		}
	}

	private void addDependencyToUi() {
		projectOperations.addDependency(webModule.getModuleName(), addDependency(projectOperations.getTopLevelPackage("security").toString(), "security", projectOperations.getPomFromModuleName("security").getVersion()));		
	}	

	//TODO: Refactor into utilities jar
	/**
	 * A utility method to convert an input stream to a String.
	 * 
	 * @param source
	 *            The source input stream.
	 * @return The source as a String, or an empty String if the source is null
	 * @throws IOException
	 *             If unable to read stream.
	 */
	private String convertStreamToString(InputStream source) throws IOException {
		/*
		 * To convert the InputStream to String we use the Reader.read(char[]
		 * buffer) method. We iterate until the Reader returns -1 which means
		 * there's no more data to read. We use the StringWriter class to
		 * produce the string.
		 */
		if (source != null) {
			Writer writer = new StringWriter();

			char[] buffer = new char[1024];
			try {
				Reader reader = new BufferedReader(new InputStreamReader(
						source, "UTF-8"));
				int n;
				while ((n = reader.read(buffer)) != -1) {
					writer.write(buffer, 0, n);
				}
			} finally {
				source.close();
			}
			return writer.toString();
		} else {
			return "";
		}
	}

	private Dependency addDependency(String groupId, String artifactId,
			String version) {
		return new Dependency(groupId, artifactId, version, DependencyType.JAR,
				DependencyScope.COMPILE);
	}
}